import datetime
import os.path
import re
import subprocess
import sys
import time
import configparser
from io import StringIO
import requests
from lxml import etree
from pushbullet import PushBullet


dir_path = os.path.dirname(os.path.realpath(__file__))
config = configparser.ConfigParser()
config.read(f'{dir_path}/config.ini', encoding='utf-8')

link = config['DEFAULT']['link']
file_name = f"{dir_path}/{config['DEFAULT']['file_name']}"
data = config['DEFAULT']['data']
text = config['DEFAULT']['text']

debug = config['Changeable'].getboolean('debug')
delay_period_sec = int(config['Changeable']['delay_period_sec'])
Message_type = config['Changeable']['Message_type']
tcpdump_interface = config['Changeable']['tcpdump_interface']
Message_method = config['Changeable']['Message_method']

pb_access_token = config['Pushbullet']['pb_access_token']

command = """addr=$(host nowere.net | awk '/has address/ {print$4}');
addr="dst ""${addr}"" and (port 80 or port 443)";
tcpdump -i """ + tcpdump_interface + """ -tttt -nn "${addr}" -l """


def block_print():
    sys.stdout = open(os.devnull, 'w')


if not debug:
    block_print()


def latest_post(tree):
    refs = tree.xpath("//a[@name]")
    post_number = [link.get('name', '') for link in refs]
    current_post = max(post_number)
    return int(current_post)


def long_post(link, number):
    thread_request = requests.get(link)
    thread_request.encoding = 'utf-8'
    # tree = etree.parse(StringIO(thread_request.content.decode("utf-8")), parser=etree.HTMLParser())
    long_text, image = parse_post(number, thread_request.text)
    return long_text, image


def parse_post(number, text):
    if Last_reported < number:
        root = etree.HTML(text)
        path = root.find('.//td[@id="reply%d"]' % number)
        OP_post_tag = 0
        if path is None:
            OP_post_tag = 1
        post_file_url = None
        blockquote_mark = long_post_mark = 0
        post_text = str()
        post_file_path = None
        if OP_post_tag == 1:
            path = root.find('.//form[@id="delform"]')
        for i in path.iter():
            if long_post_mark == 1:
                long_post_mark = 0
                post_text, post_file_path = long_post(f'https://nowere.net{i.attrib.get("href")}', number)
                return str(post_text), post_file_path
            if i.attrib.get('class') == 'filetitle':
                thread_name = i.text
                post_text = post_text + f'New thread started, title : {thread_name}\n'
            elif i.attrib.get('target') == '_blank':
                if Message_type == 'text_file':
                    if post_file_url is None:
                        post_file_url = f'https://nowere.net{i.attrib.get("href")}'
                        post_file_name = post_file_url[25:]
                        post_file_request = requests.get(f'{post_file_url}')
                        with open(f'{dir_path}/post_files/{post_file_name}', 'wb') as post_file:
                            post_file.write(post_file_request.content)
                        post_file_path = f'{dir_path}/post_files/{post_file_name}'
                        post_text = post_text + f'File attached with post, name: {post_file_name}\n'
                    else:
                        continue
                else:
                    continue
            elif i.attrib.get('class') == 'omittedposts':
                break
            elif i.tag == 'table':
                break
            elif i.attrib.get('clear') == 'left':
                break
            elif i.tag == 'blockquote':
                blockquote_mark = 1
            if blockquote_mark == 1:
                if i.tag == 'div':
                    blockquote_mark = 0
                    long_post_mark = 1
                    continue
                if i.text is not None:
                    post_text = post_text + f'{i.text}\n'
                if i.tail:
                    i.tail = i.tail.strip('\n')
                    i.tail = i.tail.strip()
                    if i.tail:
                        post_text = post_text + f'{i.tail}\n'
        return str(post_text), post_file_path
    else:
        return None, None


def nowere_request():
    global html_page, tree, Error_text, Status, Max_No, post_text, post_file, chan_status_notified, start, post_file_send, text_send, text

    try:
        html_page = requests.get(link, timeout=5)
        html_page.encoding = 'utf-8'
        tree = etree.parse(StringIO(html_page.content.decode("utf-8")), parser=etree.HTMLParser())
        # f = urllib.request.urlopen(link)  # может иметь таймаут вплоть до 30 секунд, 10 не подойдёт, начнётся дос
        print(f'Request {request_time}')  # debug
    except requests.exceptions.HTTPError as e:
        # Error_flag = True
        Error_text = 'The server couldn\'t fulfill the request.\nError code: ' + str(e.errno)
        Status = "down"
    except requests.exceptions.ConnectionError as e:
        # Error_flag = True
        Error_text = 'We failed to reach a server.\nReason: ' + str(e.args[0].reason)
        Status = "down"
    else:
        # site = f.read().decode('utf-8')
        # last_post = re.findall(r'<span .*<a .*No\.(\d+)</a>.*</span>', site)
        # last_post = re.findall(r'<span class="reflink"> <a href=".{0,100}">No\.(\d+)</a>  </span>', site)
        # Max_No = max(list(map(int, last_post)))
        Max_No = latest_post(tree)
        Status = "up"

    if Status == 'up' and chan_status_notified == 0:
        post_text, post_file = parse_post(Max_No, html_page.text)
        if post_file is not None:
            post_file_send = True
        if post_text is not None:
            text_send = True
        print("Max No ", Max_No)
    elif Status == 'down' and chan_status_notified == 0:
        print('chan is down!\n' + Error_text)
        text = 'chan is down!\n' + Error_text
        push = pb.push_note(data, text)
        start = time.time()
        chan_status_notified = 1
    elif Status == 'down' and chan_status_notified == 1:
        pass
    elif Status == 'up' and chan_status_notified == 1:
        chan_status_notified = 0
        print('chan is UP again!')
        text = 'chan is UP again!'
        push = pb.push_note(data, text)
        start = time.time()


def tcp_loop():

    tcp_loop_state = True
    # ------------------------------------------
    tcp_viewed = False
    while tcp_loop_state:
        # first iteration always produces empty byte string in non-blocking mode
        line = p.stdout.readline()
        if line != b'':
            visit_time_string = re.search(r'\d{4}-\d{2}-\d{2} \d{1,2}:\d{1,2}:\d{1,2}\.\d*',
                                          line.decode('utf-8')).group(0)
            visit_time = datetime.datetime.strptime(visit_time_string, '%Y-%m-%d %H:%M:%S.%f')
            # visit_time_list = list(map(int,re.split(':', visit_time_string)))
            # visit_time = visit_time_list[0] * 3600 + visit_time_list[1] * 60 + visit_time_list[2]

            if not disable_time_start < visit_time < disable_time_end:
                if not tcp_viewed:
                    print(f'tcpdump {visit_time_string}')
                tcp_viewed = True
                # print(visit_time_string)

        else:
            tcp_loop_state = False
    return tcp_viewed


def send_notification(post_text, post_file):
    if Message_type == 'notif':
        post_text = str(New_posts) + ' new posts'
        push = pb.push_note(data, post_text)
    elif Message_type == 'text_only':
        push = pb.push_note(data, post_text)
    elif Message_type == 'text_file':
        if post_file is not None:
            with open(f'{post_file}', "rb") as _post_file:
                file_data = pb.upload_file(_post_file, f'{post_file[11:]}')
            push_post_file = pb.push_file(**file_data)
            push = pb.push_note(data, post_text)
        else:
            push = pb.push_note(data, post_text)


try:
    file = open(file_name, 'r')
    Last_viewed = int(re.search(r'Last_viewed = (\d+)', file.readline()).group(1))
    Status = re.search(r'Status = "(down|up)"', file.readline()).group(1)
    file.close()
except Exception as e:
    with open(file_name, 'w') as file:
        file.write('Last_viewed = 0\nStatus = "down"\n')
    Last_viewed = 0
    Status = 'down'

pb = PushBullet(pb_access_token)

print(f'Start mode is {Message_type}')
print('Last_viewed = ', Last_viewed)
print('Status = "' + Status + '"')
Last_reported = Max_No = Last_viewed

# flag_reset_tcpdump = 0
if Message_type == 'notif':
    pre_utc_dt = datetime.datetime.utcnow()
    p = subprocess.Popen(command, shell=True, bufsize=-1, stdout=subprocess.PIPE)
    os.set_blocking(p.stdout.fileno(), False)
start = time.time()
delay = chan_status_notified = 0
post_text = post_file = None
post_file_send = text_send = False


while True:
    # ------------------------------------------
    if time.time() > start + delay_period_sec:

        disable_time_start = datetime.datetime.now()

        request_time = datetime.datetime.utcnow()  # debug

        nowere_request()

        time.sleep(0.6)  # в этой версии код выполняется быстрее и без слипа tcp_loop работает через жопу
        disable_time_end = datetime.datetime.now()

        start = time.time()


    # tcp_viewed = tcp_loop()

    if Message_type == 'notif':
        if tcp_loop():
            if Last_viewed < Max_No:
                Last_viewed = Max_No
                with open(file_name, 'w') as file:
                    file.write(f'Last_viewed = {Last_viewed}\nStatus = "{Status}"\n')

    if Last_viewed < Max_No:
        if Last_reported < Max_No:
            New_posts = Max_No - Last_viewed
            print(str(New_posts) + ' new posts')

            if text_send or post_file_send:
                send_notification(post_text, post_file)

            Last_reported = Max_No

        with open(file_name, 'w') as file:
            file.write(f'Last_viewed = {Last_viewed}\nStatus = "{Status}"\n')

    # ------------------------------------------
    if Message_type == 'notif':
        utc_dt = datetime.datetime.utcnow()
        if (utc_dt.hour == 21) and (pre_utc_dt.hour == 20):
            p.terminate()
            p = subprocess.Popen(command, shell=True, bufsize=-1, stdout=subprocess.PIPE)
            os.set_blocking(p.stdout.fileno(), False)
            print('TCPDUMP reset')
            push = pb.push_note(data, 'TCPDUMP reset')
        pre_utc_dt = utc_dt
    time.sleep(0.5)

p.terminate()
